/*****************************************************************************/
/* File: SoundManager.cpp				                                     */
/* Author: rszulgo                                                           */
/*                                                                           */
/* Description:																 */
/* The sound manager avoid a same sound to be loaded multiple				 */
/* times. It keeps a map containing all the already loaded sounds.			 */
/*****************************************************************************/
#include "SoundManager.h"
#include "bass.h"
#include "Common.h"

#define MESS(id,m,w,l) SendDlgItemMessage(g_hWnd,id,m,(WPARAM)(w),(LPARAM)(l))
#define STLM(m,w,l) MESS(10,m,w,l)
#define GETSTR() STLM(LB_GETCURSEL,0,0)

CSoundManager::CSoundManager() : m_mSounds()
{
}

// Deletes all loaded sounds
CSoundManager::~CSoundManager()
{
	TSoundMap::iterator iter = m_mSounds.begin();
	for (iter; iter != m_mSounds.end(); iter++)
	{
		if (iter->second)
			delete iter->second;
	}
}

// Returns the unique class instance.
CSoundManager* CSoundManager::GetInstance()
{
	static CSoundManager Instance;
	return &Instance;
}

void CALLBACK LoopSyncProc(HSYNC handle, DWORD channel, DWORD data, void *user)
{
	BASS_ChannelSetPosition(channel,0,BASS_POS_BYTE); // failed, go to start of file instead
}

// load file
int CSoundManager::LoadFile(const std::string& strFileName)
{
	HSTREAM str;
	SSoundData *soundData = new SSoundData;
	soundData->strs = NULL;
	if (str=BASS_StreamCreateFile(FALSE,strFileName.c_str(),0,0,0)) {
		soundData->strs = (HSTREAM*)realloc((void*)soundData->strs,sizeof(str));
		soundData->strs[0]=str;
		STLM(LB_ADDSTRING,0,strrchr(strFileName.c_str(),'\\')+1);
		m_mSounds[strFileName] = soundData;
	} 
	else
		Error("Can't open stream");
	
	return 0;
}

// Returns sound object for a given file name
bool CSoundManager::AddSound(const std::string& strSoundName)
{
	// Look in the map if the sound is already loaded.
	TSoundMap::const_iterator iter = m_mSounds.find(strSoundName);
	if (iter != m_mSounds.end()) {
		iter->second;
		return false;
	}

	// If it was not found, try to load it from file. If the load 
	// failed, delete the sound and throw an exception.
	else
		LoadFile(strSoundName);
	
	return true;
}

// Removes sound from sounds map
bool CSoundManager::ReleaseSound(const std::string& strSoundName)
{
	// Retrieve the sound from the map
	bool bFound = false;
	TSoundMap::iterator iter = m_mSounds.find(strSoundName);
	if (iter != m_mSounds.end())
	{
		// If it was found, we delete it and remove the
		// pointer from the map.
		bFound = true;
		if (iter->second)
			delete iter->second;
		m_mSounds.erase(iter);
	}

	return bFound;
}

// select a file to play, and start scanning it
BOOL CSoundManager::PlaySound(const std::string& strSoundName, bool loop)
{
	AddSound(strSoundName);
	m_mSounds[strSoundName]->strs[0]=BASS_StreamCreateFile(FALSE,strSoundName.c_str(),0,0,0);
	LRESULT s=GETSTR();
	if (s!=LB_ERR)
		if (loop) 
		{
			BASS_ChannelSetSync(m_mSounds[strSoundName]->strs[s],BASS_SYNC_END|BASS_SYNC_MIXTIME,0,LoopSyncProc,0); // set sync to loop at end

		}
		if (!BASS_ChannelPlay(m_mSounds[strSoundName]->strs[0],FALSE)) // play the stream (continue from current position)
			Error("Can't play stream");
	return TRUE;
}

// stop play
BOOL CSoundManager::StopSound(const std::string& strSoundName)
{
	LRESULT s=GETSTR();
	if (s!=LB_ERR) BASS_ChannelStop(m_mSounds[strSoundName]->strs[s]); // stop the stream
	return TRUE;
}

void CSoundManager::RestartSound(const std::string& strSoundName, bool loop)
{
	AddSound(strSoundName);
	LRESULT s=GETSTR();
	if (loop) 
	{
		BASS_ChannelSetSync(m_mSounds[strSoundName]->strs[s],BASS_SYNC_END|BASS_SYNC_MIXTIME,0,LoopSyncProc,0); // set sync to loop at end
	} 
	if (s!=LB_ERR) BASS_ChannelPlay(m_mSounds[strSoundName]->strs[s],TRUE); // play the stream from the start
}

// display error messages
void CSoundManager::Error(const char *es)
{
	char mes[200];
	sprintf_s(mes, 200, "%s\n(error code: %d)",es,BASS_ErrorGetCode());
	MessageBox(g_hWnd,mes,0,0);
}

